package com.chris.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;

/**
 * oauth2.0认证中心相关配置类
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private TokenStore tokenStore;
    @Autowired
    private ClientDetailsService clientDetailsService;
    @Autowired
    private AuthenticationManager authenticationManager;

    /**
     * 配置令牌访问安全约束
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                //开启/oauth/token_key 验证端口认证权限访问
                .tokenKeyAccess("permitAll()")
                //开启/oauth/check_token验证端口认证权限访问
                .checkTokenAccess("permitAll()")
                //表示支持client_id和client_secret做登录认证
                .allowFormAuthenticationForClients();
    }

    /**
     * 配置允许向认证中心授权的客户端，也有多种存储方式，如内存、redis、数据库等等
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //暂用内存方式
        clients.inMemory()
                .withClient("clientOne") //客户端id
                .secret(new BCryptPasswordEncoder().encode("chris123")) //客户端密钥
                .resourceIds("res1") //资源id，表示允许客户端访问的资源，如订单服务，代表一个资源
                .authorizedGrantTypes("authorization_code","password", "client_credentials","implicit","refresh_token") // /授权模式，总共四种,1.authorization_code(授权码模式)、password(密码模式)、client_credentials(客户端模式)、implicit(简化模式),refresh_token并不是授权模式，表示认证中心支持token刷新
                .scopes("all")//允许的授权范围，all只是一种标识，可以自定义，为了后续资源服务进行权限控制
                .autoApprove(false)//是否自动授权，否则会跳到重定向页面
                .redirectUris("https://www.baidu.com"); //授权码模式的回调地址

    }

    /**
     * 配置令牌访问的端点
     * spring Security框架默认的访问端点有如下6个：/oauth/authorize：获取授权码的端点/oauth/token：获取令牌端点。/oauth/confifirm_access：用户确认授权提交端点。/oauth/error：授权服务错误信息端点。/oauth/check_token：用于资源服务访问的令牌解析端点。/oauth/token_key：提供公有密匙的端点，如果你使用JWT令牌的话。
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //授权码模式所需要的authorizationCodeServices
        endpoints.authorizationCodeServices(authorizationCodeServices())
                //密码模式需要的 authenticationManager
                .authenticationManager(authenticationManager)
                //令牌管理服务，无论哪种模式都需要
                .tokenServices(tokenServices())
                //允许刷新token重复使用
                .reuseRefreshTokens(true)
                //只允许post方式提交访问令牌 如/oauth/token
                .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST);
    }

    /**
     * 授权码服务配置，使用授权码模式必须配置授权码服务，用来颁布和删除授权码，同样支持多种存储方式
     * @return
     */
    @Bean
    public AuthorizationCodeServices authorizationCodeServices(){
        return new InMemoryAuthorizationCodeServices();
    }

    /**
     * 令牌管理服务配置
     * @return
     */
    @Bean
    public AuthorizationServerTokenServices tokenServices(){
        DefaultTokenServices services = new DefaultTokenServices();
        //客户端配置策略
        services.setClientDetailsService(clientDetailsService);
        //token存储策略
        services.setTokenStore(tokenStore);
        //token过期时间
        services.setAccessTokenValiditySeconds(60*60*2);
        //刷新token的过期时间
        services.setRefreshTokenValiditySeconds(60*60*24*3);
        //支持刷新token
        services.setSupportRefreshToken(true);
        return services;
    }
}
